package com.aaron.nettystudy.serialize;

import com.aaron.nettystudy.entity.RpcRequest;
import com.aaron.nettystudy.entity.RpcResponse;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * @ClassName KyroSerializer
 * @description:
 * @author: 荣燊
 * @create: 2021-05-14 22:28
 **/

/**
 * 由于kyro不是线程安全的。所以每个线程都应该有自己的kryo/input/output实例。
 * 所以使用ThreadLocal存放kryo对象。
 */
public class KyroSerializer implements Serializer {
    //kryo设置
    private static final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        kryo.register(RpcRequest.class);
        kryo.register(RpcResponse.class);
        kryo.setReferences(true); //是否关闭注册行为
        kryo.setRegistrationRequired(false); //是否关闭循环引用，关闭之后提升性能
        return kryo;
    });

    //序列化，对象转换为字节数组
    @Override
    public byte[] serialize(Object object) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             Output output = new Output(byteArrayOutputStream)) {
            Kryo kryo = kryoThreadLocal.get();

            //将object对象转换为byte数组
            kryo.writeObject(output, object); //kryo在处理，将结果写入output
            kryoThreadLocal.remove();
            return output.toBytes();

        } catch (Exception e) {
            throw  new SerializeException("序列化失败");
        }
    }

    //反序列化，将字节数组转换为特定的对象T。Class<T> clazz相当于clazz = T.class
    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
             Input input = new Input(byteArrayInputStream)) {
            Kryo kryo = kryoThreadLocal.get();

            //从byte数组中反序列化出对象。可以进去看看readObject方法的源码
            Object object = kryo.readObject(input, clazz);//kryo在处理，将结果写入input,反序列化的对象类型是clazz
            kryoThreadLocal.remove();
            return clazz.cast(object); //强制将object对象装换为clazz类型

        } catch (Exception e) {
            throw  new SerializeException("反序列化失败");
        }
    }
}
